#include "Graphics/Engine.Graphics.h"

using namespace Engine;
using namespace Base;
using namespace Graphics;
using namespace Primitives;

#include "Utils/Converter/Converter.h"

DECL_RESOURCE_CONVERTER( "_bin/Engine.Utils.Converter.dll" );

#undef  APP_NAME
#define APP_NAME	TestApp
#undef  APP_BASE
#define APP_BASE	Application


class APP_NAME : public APP_BASE
{
	typedef SHARED_POINTER_TYPE( APP_NAME )	TestAppPtr;

protected:
	BatchRender				batch;
	GraphicsEngine			graphicsEngine;
	ResourceManager			resMngr;

	TimeProfiler<double>	fpsTimer;
	uint					frameCounter;

	bool					entered;
	bool					inited;
	bool					quit;


public:
	APP_NAME (IPlatform *platform, EDevice::type deviceType) :
		APP_BASE( platform, deviceType ),
		resMngr(ESS()),	graphicsEngine(ESS()), batch(ESS()),
		frameCounter(0), entered(false), inited(false), quit(false)
	{
		ESS()->GetEventSystem()->AttachEventListener(
			DelegateBuilder::Create( TestAppPtr(this), &APP_NAME::_OnAppEvent ),	ESystemEvent::APPLICATION );

		ESS()->GetEventSystem()->AttachEventListener(
			DelegateBuilder::Create( TestAppPtr(this), &APP_NAME::_OnUpdate ),		ESystemEvent::UPDATE );

		graphicsEngine.SetBatchRender( &batch );

		DESCTOP_ONLY(
			CHECK( ResConv::Run( "Engine", "_data/log.html", "_res/res_converter.ocf" ) );

			ESS()->GetFileSystem()->FindAndSetCurrentDir( "Engine/_data", 6 );
		)
		
		fpsTimer.Start();
	}


	~APP_NAME ()
	{
		ESS()->GetEventSystem()->DetachEventListener(
			DelegateBuilder::Create( TestAppPtr(this), &APP_NAME::_OnAppEvent ),	ESystemEvent::APPLICATION );

		ESS()->GetEventSystem()->DetachEventListener(
			DelegateBuilder::Create( TestAppPtr(this), &APP_NAME::_OnUpdate ),		ESystemEvent::UPDATE );
	}
	

	virtual void Update (float dt)
	{
		if ( ESS()->GetInput()->IsKeyClicked( EKey::BACK ) )
		{
			quit = true;
			GetPlatform()->Terminate();
		}

		++frameCounter;

		if ( fpsTimer.GetTimeDelta().GetSec() > 5.0 )
		{
			float fps = float( frameCounter / fpsTimer.GetTimeDelta().GetSec() );
			DEBUG_CONSOLE( (string("FPS: ") << string().FormatF( fps, StringFormatF().Fmt(0,1) )).cstr() );

			frameCounter = 0;
			fpsTimer.Start();
		}
	}


	virtual void OnInit ()
	{
		MOBILE_ONLY(
			//ESS()->GetFileSystem()->SetCurrentDirectory( GetPlatform()->GetDirectoryPath( EDirectory::INTERNAL ) );
			ESS()->GetFileSystem()->SetCurrentDirectory( GetPlatform()->GetDirectoryPath( EDirectory::EXTERNAL ) );
		)

		_GetLogger()->Open( "log/log", false );

		graphicsEngine.Init( VideoSettings() );
		graphicsEngine.InitResources();
	}


	virtual void OnEnter ()
	{
	}


	virtual void OnExit ()
	{
		ESS()->GetGraphicsEngine()->GetShaderManager()->DeleteDefaultPrograms();
		ESS()->GetResourceManager()->Clear();
	}


	void _OnUpdate (const SysEvent &ev)
	{
		if ( ev.update.renderAvailable )
		{
			ESS()->GetInput()->PreUpdate();
			Update( ev.update.timeDelta );
			ESS()->GetInput()->PostUpdate();
		}
		else
		{
			Thread::Delay( 10 );
		}
	}
	

	void _OnAppEvent (const SysEvent &ev)
	{
#		define WARN( _text )			LOG( _text, ELog::DEBUG )

		switch ( ev.app.type )
		{
			case SysEvent::Application::CREATED :
				WARN( "Application Created" );
				break;
				
			case SysEvent::Application::DESTROY :
				WARN( "Application Destroy" );
				MOBILE_ONLY( OnExit() );
				break;
				
			case SysEvent::Application::ENTER_BACKGROUND :
				WARN( (string("Application Enter Background ") << (ev.app.completed ? "completed" : "")).cstr() );
				break;
				
			case SysEvent::Application::ENTER_FOREGROUND :
				WARN( (string("Application Enter Foreground ") << (ev.app.completed ? "completed" : "")).cstr() );
				
				if ( ev.app.completed and not inited )
				{
					OnInit();
					inited = true;
				}
				break;
				
			case SysEvent::Application::SURFACE_CREATED :
				WARN( "Application Surface Created" );
				if ( not entered )
				{
					OnEnter();
					entered = true;
				}
				break;
				
			case SysEvent::Application::SURFACE_DESTROYED :
				WARN( "Application Surface Destroyed" );
				MOBILE_ONLY( if ( quit ) )
				{
					OnExit();
				}
				break;
				
			case SysEvent::Application::LOW_MEMORY :
				WARN( "Application Low Memory" );
				break;
		}

#		undef WARN
	}


	template <typename T>
	static void CreateApplication (IPlatform *platform, EDevice::type deviceType, IApplication *&app)
	{
		app = new T( platform, deviceType );
	}
};
